package com.heiye.count.biz.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.google.common.collect.Maps;
import com.heiye.count.api.dto.FindUserCountsByIdReqDTO;
import com.heiye.count.api.dto.FindUserCountsByIdRspDTO;
import com.heiye.count.biz.constants.RedisKeyConstants;
import com.heiye.count.biz.domain.dataobject.UserCountDO;
import com.heiye.count.biz.domain.mapper.UserCountDOMapper;
import com.heiye.count.biz.service.UserCountService;
import com.heiye.framework.common.response.Response;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author: heiye
 * @date: 2025/05/14 下午4:20
 * @version: v1.0.0
 * @description: 用户计数业务
 */
@Service
@Slf4j
public class UserCountServiceImpl implements UserCountService {

    @Resource
    private UserCountDOMapper userCountDOMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 查询用户相关计数
     *
     * @param findUserCountsByIdReqDTO
     * @return
     */
    @Override
    public Response<FindUserCountsByIdRspDTO> findUserCountData(FindUserCountsByIdReqDTO findUserCountsByIdReqDTO) {
        // 目标用户 ID
        Long userId = findUserCountsByIdReqDTO.getUserId();

        FindUserCountsByIdRspDTO findUserCountsByIdRspDTO = FindUserCountsByIdRspDTO.builder()
                .userId(userId)
                .build();

        // 先从 Redis 中查询
        String userCountHasKey = RedisKeyConstants.buildCountUserKey(userId);

        List<Object> counts = redisTemplate.opsForHash()
                .multiGet(userCountHasKey, List.of(
                        RedisKeyConstants.FIELD_QUIZ_TOTAL,
                        RedisKeyConstants.FIELD_LIKE_TOTAL,
                        RedisKeyConstants.FIELD_BROWSE_TOTAL,
                        RedisKeyConstants.FIELD_COMMENT_TOTAL,
                        RedisKeyConstants.FIELD_BEST_COMMENT_TOTAL
                ));

        // 若 Hash 中计数不为空，优先以其为主(实时性更高)
        Object quizTotal = counts.get(0);
        Object likeTotal = counts.get(1);
        Object browseTotal = counts.get(2);
        Object commentTotal = counts.get(3);
        Object bestCommentTotal = counts.get(4);

        findUserCountsByIdRspDTO.setQuizTotal(Objects.isNull(quizTotal) ? 0 : Long.parseLong(String.valueOf(quizTotal)));
        findUserCountsByIdRspDTO.setLikeTotal(Objects.isNull(likeTotal) ? 0 : Long.parseLong(String.valueOf(likeTotal)));
        findUserCountsByIdRspDTO.setBrowseTotal(Objects.isNull(browseTotal) ? 0 : Long.parseLong(String.valueOf(browseTotal)));
        findUserCountsByIdRspDTO.setCommentTotal(Objects.isNull(commentTotal) ? 0 : Long.parseLong(String.valueOf(commentTotal)));
        findUserCountsByIdRspDTO.setBestCommentTotal(Objects.isNull(bestCommentTotal) ? 0 : Long.parseLong(String.valueOf(bestCommentTotal)));

        // 若 Redis 中有任何一个计数为空
        boolean isAnyNull = counts.stream().anyMatch(Objects::isNull);

        if (isAnyNull) {
            // 从数据库查询该用户的计数
            UserCountDO userCountDO = userCountDOMapper.selectByUserId(userId);

            // 判断 Redis 中对应计数，若为空，则使用 DO 中的计数
            if (Objects.nonNull(userCountDO) && Objects.isNull(quizTotal)) {
                findUserCountsByIdRspDTO.setQuizTotal(userCountDO.getQuizTotal());
            }

            if (Objects.nonNull(userCountDO) && Objects.isNull(likeTotal)) {
                findUserCountsByIdRspDTO.setLikeTotal(userCountDO.getLikeTotal());
            }

            if (Objects.nonNull(userCountDO) && Objects.isNull(browseTotal)) {
                findUserCountsByIdRspDTO.setBrowseTotal(userCountDO.getBrowseTotal());
            }

            if (Objects.nonNull(userCountDO) && Objects.isNull(commentTotal)) {
                findUserCountsByIdRspDTO.setCommentTotal(userCountDO.getCommentTotal());
            }

            if (Objects.nonNull(userCountDO) && Objects.isNull(bestCommentTotal)) {
                findUserCountsByIdRspDTO.setBestCommentTotal(userCountDO.getBestCommentTotal());
            }

            // 异步同步到 Redis 缓存中, 以便下次查询能够命中缓存
            syncHashCount2Redis(userCountHasKey, userCountDO, quizTotal, likeTotal, commentTotal, browseTotal, bestCommentTotal);
        }

        return Response.success(findUserCountsByIdRspDTO);
    }

    /**
     * 将该用户的 Hash 计数同步到 Redis 中
     *
     * @param userCountHasKey
     * @param userCountDO
     * @param quizTotal
     * @param likeTotal
     * @param commentTotal
     * @param browseTotal
     * @param bestCommentTotal
     */
    private void syncHashCount2Redis(String userCountHasKey, UserCountDO userCountDO,
                                     Object quizTotal, Object likeTotal, Object commentTotal, Object browseTotal, Object bestCommentTotal) {
        if (Objects.nonNull(userCountDO)) {
            threadPoolTaskExecutor.submit(() -> {
                // 存放计数
                Map<String, Long> userCountMap = Maps.newHashMap();

                if (Objects.isNull(quizTotal)) {
                    userCountMap.put(RedisKeyConstants.FIELD_QUIZ_TOTAL, Objects.isNull(userCountDO.getQuizTotal()) ? 0 : userCountDO.getQuizTotal());
                }

                if (Objects.isNull(likeTotal)) {
                    userCountMap.put(RedisKeyConstants.FIELD_LIKE_TOTAL, Objects.isNull(userCountDO.getLikeTotal()) ? 0 : userCountDO.getLikeTotal());
                }

                if (Objects.isNull(commentTotal)) {
                    userCountMap.put(RedisKeyConstants.FIELD_COMMENT_TOTAL, Objects.isNull(userCountDO.getCommentTotal()) ? 0 : userCountDO.getCommentTotal());
                }

                if (Objects.isNull(browseTotal)) {
                    userCountMap.put(RedisKeyConstants.FIELD_BROWSE_TOTAL, Objects.isNull(userCountDO.getBrowseTotal()) ? 0 : userCountDO.getBrowseTotal());
                }

                if (Objects.isNull(bestCommentTotal)) {
                    userCountMap.put(RedisKeyConstants.FIELD_BEST_COMMENT_TOTAL, Objects.isNull(userCountDO.getBestCommentTotal()) ? 0 : userCountDO.getBestCommentTotal());
                }

                redisTemplate.executePipelined(new SessionCallback<>() {
                    @Override
                    public Object execute(RedisOperations operations) {
                        // 批量添加 Hash 的计数 Field
                        operations.opsForHash().putAll(userCountHasKey, userCountMap);

                        // 设置随机过期时间 (2 小时以内)
                        long expireTime = 60 * 60 + RandomUtil.randomInt(60 * 60);
                        operations.expire(userCountHasKey, expireTime, TimeUnit.SECONDS);
                        return null;
                    }
                });
            });
        }
    }
}
